/*!	 synfig/rendering/common/optimizer/optimizertransformation.cpp
**	 OptimizerTransformation
**
**	......... ... 2015 Ivan Mahonin
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#ifndef _WIN32
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#endif

#include "optimizertransformation.h"

#include "../../primitive/affinetransformation.h"
#include "../task/tasktransformation.h"
#include "../task/tasktransformableaffine.h"
#include "../task/tasksolid.h"
#include "../task/taskmesh.h"

#endif

using namespace synfig;
using namespace rendering;

bool
OptimizerTransformation::can_optimize(const Task::Handle &sub_task)
{
    if (!sub_task) {
        return true;
    }

    if (sub_task.type_is<TaskSolid>()) {
        return true;
    }

    if (sub_task.type_is<TaskTransformableAffine>()) {
        return true;
    }

    if (TaskTransformation::Handle transformation = TaskTransformation::Handle::cast_dynamic(sub_task))
        if (AffineTransformation::Handle::cast_dynamic(transformation->transformation)) {
            return true;
        }

    return false;
}

void
OptimizerTransformation::calc_unoptimized_blend_brunches(int &ref_count, const Task::Handle &blend_sub_task)
{
    if (ref_count > 1) {
        return;
    }

    if (can_optimize(blend_sub_task)) {
        return;
    }

    if (TaskBlend::Handle blend = TaskBlend::Handle::cast_dynamic(blend_sub_task)) {
        calc_unoptimized_blend_brunches(ref_count, blend->sub_task_a());
        calc_unoptimized_blend_brunches(ref_count, blend->sub_task_b());
        return;
    }

    ++ref_count;
}

void
OptimizerTransformation::run(const RunParams& params) const
{
    // TODO: Optimize transformation to transformation
    if (TaskTransformation::Handle transformation = TaskTransformation::Handle::cast_dynamic(params.ref_task)) {
        // transformation of TaskSolid or null-task is meaningless
        if (!transformation->sub_task()
                || transformation->sub_task().type_is<TaskSolid>()) {
            apply(params, transformation->sub_task());
            return;
        }

        // optimize affine transformation
        if (AffineTransformation::Handle affine_transformation = AffineTransformation::Handle::cast_dynamic(transformation->transformation)) {
            if (affine_transformation->matrix.is_identity()) {
                apply(params, transformation->sub_task());
                return;
            } else if (transformation->sub_task().type_is<TaskTransformableAffine>()) {
                // apply affine transformation to sub-task
                Task::Handle task = transformation->sub_task()->clone();
                TaskTransformableAffine *transformable_affine = task.type_pointer<TaskTransformableAffine>();
                transformable_affine->transformation *=
                    affine_transformation->matrix;
                apply(params, task);
                return;
            } else if (TaskTransformation::Handle sub_transformation = TaskTransformation::Handle::cast_dynamic(transformation->sub_task())) {
                // optimize affine transformation of affine transformation
                if (AffineTransformation::Handle sub_affine_transformation = AffineTransformation::Handle::cast_dynamic(sub_transformation->transformation)) {
                    transformation = TaskTransformation::Handle::cast_dynamic(transformation->clone());
                    AffineTransformation::Handle new_affine_transformation = new AffineTransformation();
                    new_affine_transformation->matrix =
                        sub_affine_transformation->matrix
                        * affine_transformation->matrix;
                    transformation->transformation = new_affine_transformation;
                    transformation->sub_task() = sub_transformation->sub_task();

                    apply(params, transformation);
                    return;
                }
            } else if (TaskBlend::Handle blend = TaskBlend::Handle::cast_dynamic(transformation->sub_task())) {
                // optimize transfromation of TaskBlend
                // int count = 0;
                blend = TaskBlend::Handle::cast_dynamic(blend->clone());
                TaskTransformation::Handle transformation_a = TaskTransformation::Handle::cast_dynamic(transformation->clone());
                transformation_a->sub_task() = blend->sub_task_a();
                blend->sub_task_a() = transformation_a;
                TaskTransformation::Handle transformation_b = TaskTransformation::Handle::cast_dynamic(transformation->clone());
                transformation_b->sub_task() = blend->sub_task_b();
                blend->sub_task_b() = transformation_b;
                apply(params, blend);
                return;
            }
        }
    }
}